package org.example.string;



/**
 * 重点注意：intern 源码中有说，当调用者已经存在于字符串常量池中时，此时返回的是池中存在的对象，否则添加当前对象并返回当前对象的引用
 * @author youshang
 * @date 2022/03/28 09:56
 **/
public class StringIntern {

    public static void main1(String[] args) {
        //此时会创建两个对象，一个是存在于内存中的，另外一个是存在于字符串常量池中
        String s = new String("1");
        //上一步中已经将"1"存入字符串常量池中了，当再次调用intern时，此时只会返回池中的 "1"
        s.intern();
        String s2 = "1";
        //jdk 1.6 中：false  jdk 1.7/1.8 false
        //字符串常量池中是同一个对象，但是内存地址不一致 但是 结果为false(个人结论)
        //重点注意：intern 源码中有说，当调用者已经存在于字符串常量池中时，此时返回的是池中存在的对象，否则添加当前对象并返回当前对象的引用

        System.out.println(s == s2);

        //此时会创建6个对象，并且拼接结果不会存入字符串常量池中
        String s3 = new String("1") + new String("1");
        //当调用intern的时候，此时会在池中添加调用者，并且返回当前调用的引用地址
        s3.intern();
        //创建 "11"的时候，因为字符串常量池中已经存在 "11" 所以会选择直接去引用字符串常量池中的 "11"
        String s4 = "11";
        //
        System.out.println(s3 == s4);

        //jdk 1.6 中：false  jdk 1.7/1.8 true
    }

    public static void main(String[] args) {
        String s = new String("1");
        String s1 = "1";
        System.out.println(s == s1);
        /*1. 上面这个例子中总共创建了几个对象？
          2. 结果是什么？为什么？
          答：1. 三个（字符串常量池 + 内存）
             2. false, s = 内存中的引用地址 ，s1 = 常量池中的对象地址
         */


        String s2 = new String("1") + new String("2");//s2 内存地址
        String s3 = "12"; //s3 字符串常量地址
        System.out.println(s2 == s3);
        /*
        1.上面总共创建了几个对象？
        2.结果为什么？ 为什么？
        答：
        1. 对象1： String Builder
           对象2： new String("1")
           对象3： ldc 字符串常量池 "1"
           对象4： new String("2")
           对象5： ldc 字符串常量池 "2"
           对象6： 在StringBuilder 最后返回的时候会有一个toString() 会new String()
           对象7:  s3 = 12
         2. false, s2 = 内存中的引用地址 ， s3 = 常量池中的对象地址
         */

        String s4 = new String("1") + new String("1");//11
        s4.intern(); //如果当前"11"存在于字符串常量池中，返回字符串常量中的对象引用 ， 如果不存在 会去创建"11" 并返回 "11" 的内存引用地址
        String s5 = "11";
        System.out.println(s4 == s5);//  s4的内存引用地址 和 s5的内存引用地址 共用同一个
        /*
        1. 结果为什么？ 为什么？
        答：true,
            当调用intern()的时候，常量池中存在则返回常量池中对象地址，否则创建并返回去内存引用地址
            s4调用intern()方法时，此时s4只存在于内存之中，并不存在于常量池中，所有会返回s4的内存引用地址，
            再创建s5的时候，发现 "11"已经存在于常量池之中，"11" 就不会再次被创建，而是选择将s5 的引用地址映射到 s4的内存引用地址上面
         */

        String s6 = new String("1") + new String("1");
        String s7 = "11";
        s6.intern();
        System.out.println(s6 == s7);
        /*
        1. 结果为什么？ 为什么？
        答：false，
        在当前案例当中，可以发现重点是在 intern()的调用时机的不同会有什么影响
        s6创建完之后，对象是存在于内存之中，
        此时去创建s7 ，首先会去判断常量池中是否已经存在 "11" ，不存在则创建
        s6调用intern()方法时，此时字符串常量池中已经存在 "11"，只会返回常量池中的对象地址
         */


        String x = "ab";
        String s8 = new String("a") + new String("b");// s8 = ab ,ab只存在于内存之中
        String s9 = s8.intern();//常量池中的对象引用 相当于 String s9 = "ab";
        //当存在 "ab" 已经存在于常量池之中时，调用intern返回的是对象地址

        System.out.println(s8 == "ab"); //内存中的引用地址  == 常量池中的对象引用
        System.out.println(s9 == "ab");
        /*
        1. 结果为什么？ 为什么？
        答：false , true
        1. 在x中已经声明了 "ab"，此时在字符串常量池中就已经存在 "ab"
           s8中声明的 "ab" 只存在于内存之中
           s9 调用intern() ，此时只会返回当前对象在常量池中的对象引用地址
           最后的比较 ： 内存地址 == 字符串常量池中的对象地址
        2. 和 1 的主要区别点在于，s8.intern() 返回结果集被赋值到了 s9
           此时可以比作 String s9 = "ab";
           最后的比较： 内存地址 == 内存地址
         */

        String s31 = "hello"; //字符串常量地址
        String s11 = new String("he") + new String("llo"); //内存地址
        String s21 = s11.intern();//字符串常量地址
        System.out.println(s11 == s31);
        System.out.println(s11 == s21);


        String s12 = new String("he") + new String("llo"); //内存地址
        String s22 = s12.intern(); //内存地址
        String s32 = "hello"; //内存地址
        System.out.println(s12 == s32);
        System.out.println(s12 == s22);
    }




}
